package com.geek.rest;

import com.geek.Assert;
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import org.eclipse.microprofile.rest.client.RestClientDefinitionException;
import org.eclipse.microprofile.rest.client.ext.QueryParamStyle;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.core.Configuration;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.security.KeyStore;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;


public class DefaultRestBuilder implements RestClientBuilder {

    private final ClassLoader classLoader;

    private URL baseUri;

    private long connectTimeOutMillis;

    private long readTimeOutMillis;

    private ExecutorService executorService;

    private SSLContext sslContext;

    private KeyStore trustStore;

    private KeyStore keyStore;

    private String keystorePassword;

    private HostnameVerifier hostnameVerifier;
    private int proxyPort;
    private String proxyHost;
    private QueryParamStyle queryParamStyle;


    private RequestTemplateResolver requestTemplateResolver;

    public DefaultRestBuilder(ClassLoader classLoader) {
        this.classLoader = classLoader;
        requestTemplateResolver = new ReflectiveRequestTemplateResolver();
    }


    @Override
    public RestClientBuilder baseUrl(URL url) {
        this.baseUri = url;
        return this;
    }

    @Override
    public RestClientBuilder connectTimeout(long l, TimeUnit timeUnit) {
        connectTimeOutMillis = timeUnit.toMillis(l);
        return this;
    }

    @Override
    public RestClientBuilder readTimeout(long l, TimeUnit timeUnit) {
        this.readTimeOutMillis = timeUnit.toMillis(l);
        return this;
    }

    @Override
    public RestClientBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

    @Override
    public RestClientBuilder sslContext(SSLContext sslContext) {
        this.sslContext = sslContext;
        return this;
    }

    @Override
    public RestClientBuilder trustStore(KeyStore keyStore) {
        this.trustStore = keyStore;
        return this;
    }

    @Override
    public RestClientBuilder keyStore(KeyStore keyStore, String keystorePassword) {
        this.keyStore = keyStore;
        this.keystorePassword = keystorePassword;
        return this;
    }

    @Override
    public RestClientBuilder hostnameVerifier(HostnameVerifier hostnameVerifier) {
        this.hostnameVerifier = hostnameVerifier;
        return this;
    }

    @Override
    public RestClientBuilder followRedirects(boolean follow) {

        return this;
    }

    @Override
    public RestClientBuilder proxyAddress(String proxyHost, int proxyPort) {
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
        return this;
    }

    @Override
    public RestClientBuilder queryParamStyle(QueryParamStyle queryParamStyle) {
        this.queryParamStyle = queryParamStyle;
        return this;
    }

    @Override
    public <T> T build(Class<T> aClass) throws IllegalStateException, RestClientDefinitionException {
        Assert.isTrue(aClass.isInterface(), "@Path can only be specified on an interface");
        Map<Method, RequestTemplate> requestTemplates = resolveRequestTemplates(aClass);
        return (T) Proxy.newProxyInstance(classLoader, new Class[]{aClass}, new RestClientInvocationHandler(getConfiguration(), requestTemplates));
    }

    private <T> Map<Method, RequestTemplate> resolveRequestTemplates(Class<T> aClass) {
        Map<Method, RequestTemplate> requestTemplates = new LinkedHashMap<>();
        for (Method method : aClass.getMethods()) {
            RequestTemplate requestTemplate = requestTemplateResolver.resolve(aClass, method);
            if (requestTemplate != null) {
                requestTemplate.urlTemplate(baseUri + requestTemplate.getUriTemplate());
                requestTemplates.put(method, requestTemplate);
            }
        }
        return requestTemplates;
    }


    @Override
    public Configuration getConfiguration() {
        return null;
    }

    @Override
    public RestClientBuilder property(String s, Object o) {
        return null;
    }

    @Override
    public RestClientBuilder register(Class<?> aClass) {
        return null;
    }

    @Override
    public RestClientBuilder register(Class<?> aClass, int i) {
        return null;
    }

    @Override
    public RestClientBuilder register(Class<?> aClass, Class<?>... classes) {
        return null;
    }

    @Override
    public RestClientBuilder register(Class<?> aClass, Map<Class<?>, Integer> map) {
        return null;
    }

    @Override
    public RestClientBuilder register(Object o) {
        return null;
    }

    @Override
    public RestClientBuilder register(Object o, int i) {
        return null;
    }

    @Override
    public RestClientBuilder register(Object o, Class<?>... classes) {
        return null;
    }

    @Override
    public RestClientBuilder register(Object o, Map<Class<?>, Integer> map) {
        return null;
    }
}
